﻿function md5(str, w) {
    var M = [], len = 0, Len = str.length;
    var a = 0x67452301, b = 0xefcdab89, c = 0x98badcfe, d = 0x10325476;
    var A, B, C, D;
    var t = [[[0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee],
            [0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501],
            [0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be],
            [0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821]
        ], [[0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa],
            [0xd62f105d, 0x2441453, 0xd8a1e681, 0xe7d3fbc8],
            [0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed],
            [0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a]
        ], [[0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c],
            [0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70],
            [0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x4881d05],
            [0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665]
        ], [[0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039],
            [0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1],
            [0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1],
            [0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391]
        ]];
    for (var i = 0, k = 4, idx = 0, st = [], loop = true; loop; i += 16) {
        for (var j = 0, l = 0; j < 16 && (idx < Len || st.length); j++) {
            for (k = 0, M[j] = 0; k < 4 && (idx < Len || st.length); k++, l++) {
                if (st.length) {
                    M[j] |= st.pop() << k * 8;
                } else {
                    var ch = str.charCodeAt(idx++);
                    if (ch > 0xFF) {
                        st.push(ch & 0xFF);
                        M[j] |= (ch >>> 8) << k * 8;
                    } else {
                        M[j] |= ch << k * 8;
                    }
                }
            }
        }
        if (l < 64 && !len) {
            if (k == 4) {
                M[j++] = 0x80;
            } else {
                M[j - 1] |= 0x80 << k * 8;
            }
            len = len || i * 4 + l;
        }
        for (; j < 16; j++) {
            M[j] = 0;
        }
        if (l < 56) {
            M[14] = len * 8;
            loop = false;
        }
        A = a; B = b; C = c; D = d;
        for (var j = 0, m = 0; j < 4; j++) {
            a = FF(a, b, c, d, M[m++], 07, t[0][j][0]);
            d = FF(d, a, b, c, M[m++], 12, t[0][j][1]);
            c = FF(c, d, a, b, M[m++], 17, t[0][j][2]);
            b = FF(b, c, d, a, M[m++], 22, t[0][j][3]);
        }
        for (var j = 0, m = 1; j < 4; j++, m += 20) {
            a = GG(a, b, c, d, M[(m + 00) % 16], 05, t[1][j][0]);
            d = GG(d, a, b, c, M[(m + 05) % 16], 09, t[1][j][1]);
            c = GG(c, d, a, b, M[(m + 10) % 16], 14, t[1][j][2]);
            b = GG(b, c, d, a, M[(m + 15) % 16], 20, t[1][j][3]);
        }
        for (var j = 0, m = 5; j < 4; j++, m += 12) {
            a = HH(a, b, c, d, M[(m + 0) % 16], 04, t[2][j][0]);
            d = HH(d, a, b, c, M[(m + 3) % 16], 11, t[2][j][1]);
            c = HH(c, d, a, b, M[(m + 6) % 16], 16, t[2][j][2]);
            b = HH(b, c, d, a, M[(m + 9) % 16], 23, t[2][j][3]);
        }
        for (var j = 0, m = 0; j < 4; j++, m += 28) {
            a = II(a, b, c, d, M[(m + 00) % 16], 06, t[3][j][0]);
            d = II(d, a, b, c, M[(m + 07) % 16], 10, t[3][j][1]);
            c = II(c, d, a, b, M[(m + 14) % 16], 15, t[3][j][2]);
            b = II(b, c, d, a, M[(m + 21) % 16], 21, t[3][j][3]);
        }
        a = sum(a, A);
        b = sum(b, B);
        c = sum(c, C);
        d = sum(d, D);
    }
    if (w == 32) { return hex(a) + hex(b) + hex(c) + hex(d); };
    if (w == 16) { return hex(b) + hex(c) };
    return hex(a) + hex(b) + hex(c) + hex(d);
    /******************************************************************************************************************/
    /* MD5 rounds functions */
    function FF(A, B, C, D, M, S, T) {
        return sum(shift(sum(A, F(B, C, D), M, T), S), B);
    }
    function GG(A, B, C, D, M, S, T) {
        return sum(shift(sum(A, G(B, C, D), M, T), S), B);
    }
    function HH(A, B, C, D, M, S, T) {
        return sum(shift(sum(A, H(B, C, D), M, T), S), B);
    }
    function II(A, B, C, D, M, S, T) {
        return sum(shift(sum(A, I(B, C, D), M, T), S), B);
    }
    function F(X, Y, Z) {
        return (X & Y) | ((~X) & Z);
    }
    function G(X, Y, Z) {
        return (X & Z) | (Y & (~Z));
    }
    function H(X, Y, Z) {
        return X ^ Y ^ Z;
    }
    function I(X, Y, Z) {
        return Y ^ (X | (~Z));
    }
    /* sum, handling overflows correctly */
    function sum(x, y) {
        var args = arguments;
        var s = (x & 0x7FFFFFFF) + (y & 0x7FFFFFFF) ^ x & 0x80000000 ^ y & 0x80000000;
        for (var i = 2; i < args.length; i++) {
            s = (s & 0x7FFFFFFF) + (args[i] & 0x7FFFFFFF) ^ s & 0x80000000 ^ args[i] & 0x80000000;
        }
        return s;
    }
    function shift(x, y) {
        return x << y | x >> 32 - y & Math.pow(2, y) - 1;
    }
    /* convert integer to hex string */
    function hex(v) {
        var h = "", b;
        for (var i = 0; i <= 3; i++) {
            b = v >>> i * 8 & 0xFF | 0x100;
            h += b.toString(16).slice(-2);
        }
        return h;
    }
}
